package gps

import "math"

// GPS包实现GPS的一些常用计算
type GPS struct {
}

// 地球半径
const EarthRadius float64 = 6378.137

// 一个常量值，不知道如何命名
const Angle float64 = math.Pi / 180.0

// 计算两个GPS点之间的距离，单位：公里(km)
func (p *GPS) Distance(lat1, lng1, lat2, lng2 float64) float64 {
	if lat1 == lat2 && lng1 == lng2 {
		return 0
	}
	//radius := 6378.137
	//rad := math.Pi / 180.0
	lat1 = lat1 * Angle
	lng1 = lng1 * Angle
	lat2 = lat2 * Angle
	lng2 = lng2 * Angle
	theta := lng2 - lng1
	dist := math.Acos(math.Sin(lat1)*math.Sin(lat2) + math.Cos(lat1)*math.Cos(lat2)*math.Cos(theta))
	return dist * EarthRadius
}

const a float64 = 6378245.0
const ee float64 = 0.00669342162296594323

// 判断GPS是否在国外
func (p *GPS) OutOfChina(lat float64, lon float64) bool {
	if lon < 72.004 || lon > 137.8347 {
		return true
	}
	if lat < 0.8293 || lat > 55.8271 {
		return true
	}
	return false
}

func (p *GPS) transformLat(x float64, y float64) float64 {
	fx := x
	if x < 0 {
		fx = -x
	}
	ret := -100.0 + 2.0*x + 3.0*y + 0.2*y*y + 0.1*x*y + 0.2*math.Sqrt(fx)
	ret += (20.0*math.Sin(6.0*x*math.Pi) + 20.0*math.Sin(2.0*x*math.Pi)) * 2.0 / 3.0
	ret += (20.0*math.Sin(y*math.Pi) + 40.0*math.Sin(y/3.0*math.Pi)) * 2.0 / 3.0
	ret += (160.0*math.Sin(y/12.0*math.Pi) + 320*math.Sin(y*math.Pi/30.0)) * 2.0 / 3.0
	return ret
}

func (p *GPS) transformLon(x float64, y float64) float64 {
	fx := x
	if x < 0 {
		fx = -x
	}
	ret := 300.0 + x + 2.0*y + 0.1*x*x + 0.1*x*y + 0.1*math.Sqrt(fx)
	ret += (20.0*math.Sin(6.0*x*math.Pi) + 20.0*math.Sin(2.0*x*math.Pi)) * 2.0 / 3.0
	ret += (20.0*math.Sin(x*math.Pi) + 40.0*math.Sin(x/3.0*math.Pi)) * 2.0 / 3.0
	ret += (150.0*math.Sin(x/12.0*math.Pi) + 300.0*math.Sin(x/30.0*math.Pi)) * 2.0 / 3.0
	return ret
}

// 原始坐标系转换为GCJ-02（火星坐标系统）
func (p *GPS) TransformFromWGSToGCJ(lat float64, lng float64) (float64, float64) {
	if lat == 0 || lng == 0 {
		return lat, lng
	}
	if p.OutOfChina(lat, lng) {
		return lat, lng
	}

	dLat := p.transformLat(lng-105.0, lat-35.0)
	dLon := p.transformLon(lng-105.0, lat-35.0)
	radLat := lat / 180.0 * math.Pi
	magic := math.Sin(radLat)
	magic = 1 - ee*magic*magic
	sqrtMagic := math.Sqrt(magic)
	dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * math.Pi)
	dLon = (dLon * 180.0) / (a / sqrtMagic * math.Cos(radLat) * math.Pi)
	lat = lat + dLat
	lng = lng + dLon

	return lat, lng
}

// GCJ-02（火星坐标系统）转换为原始坐标系
func (p *GPS) TransformFromGCJToWGS(lat float64, lng float64) (float64, float64) {
	if lat == 0 || lng == 0 {
		return lat, lng
	}

	wgLat, wgLng := lat, lng
	var currLat, currLng, dLat, dLng float64
	for {
		currLat, currLng = p.TransformFromWGSToGCJ(wgLat, wgLng)
		dLat = lat - currLat
		dLng = lng - currLng
		if math.Abs(float64(dLat)) < 1e-7 && math.Abs(float64(dLng)) < 1e-7 { // 1e-7 ~ centimeter level accuracy
			return wgLat, wgLng
		}
		wgLat += dLat
		wgLng += dLng
	}

	return wgLat, wgLng
}

// /
// /  Transform GCJ-02 to BD-09
// /
const xPi float64 = 3.14159265358979324 * 3000.0 / 180.0

// GCJ-02（火星坐标系统）转换为百度地图坐标
func (p *GPS) BDEncrypt(lat float64, lng float64) (float64, float64) {
	if lat == 0 || lng == 0 {
		return lat, lng
	}

	x := lng
	y := lat
	z := math.Sqrt(x*x+y*y) + 0.00002*math.Sin(y*xPi)
	theta := math.Atan2(y, x) + 0.000003*math.Cos(x*xPi)
	//return LocationMake(, );
	return z*math.Sin(theta) + 0.006, z*math.Cos(theta) + 0.0065
}

//	Transform BD-09 to GCJ-02
//
// 百度坐标转换为GCJ-02（火星坐标系统）
func (p *GPS) BDDecrypt(lat float64, lng float64) (float64, float64) {
	if lat == 0 || lng == 0 {
		return lat, lng
	}

	x := lng - 0.0065
	y := lat - 0.006
	z := math.Sqrt(x*x+y*y) - 0.00002*math.Sin(y*xPi)
	theta := math.Atan2(y, x) - 0.000003*math.Cos(x*xPi)
	return z * math.Sin(theta), z * math.Cos(theta)
}
